﻿using System.Security.Cryptography;
using System;

namespace SharpCrack.HashAlgorithms
{
	/// <summary>
	/// Computes the CRC32 value for the input data.
	/// </summary>
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CRC")]
	public sealed class CRC16 : HashAlgorithm
	{
		public CRC16()
			: this(DefaultPolynomial)
		{ }
		public CRC16(ushort ulPolynomial)
		{
			this.Polynomial = ulPolynomial;
			this.HashSizeValue = 16;

			this.Initialize();
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public static new CRC16 Create()
		{
			return new CRC16();
		}

		/// <summary>
		/// 
		/// </summary>
		public static ushort DefaultPolynomial
		{
			get { return 0xA001; }
		}

		/// <summary>
		/// 
		/// </summary>
		public ushort Polynomial { get; set; }

		private ushort Value = 0xFFFF;

		private ushort[] table;

		public override void Initialize()
		{
			BuildCRC16Table(this.Polynomial);
		}

		private void BuildCRC16Table(ushort ulPolynomial)
		{
			ushort dwCrc;
			table = new ushort[256];

			for (ushort i = 0; i < table.Length; i++)
			{
				ushort val = 0;
				dwCrc = i;

				for (byte j = 0; j < 8; j++)
				{
					if (((val ^ dwCrc) & 0x0001) != 0)
						val = (ushort)((val >> 1) ^ ulPolynomial);
					else
						val >>= 1;

					dwCrc >>= 1;
				}

				table[i] = val;
			}
		}

		protected override void HashCore(byte[] array, int ibStart, int cbSize)
		{
			this.Value = 0;

			for (int i = ibStart; i < cbSize; i++)
			{
				byte tabPtr = (byte)((this.Value & 0xFF) ^ array[i]);
				this.Value = (ushort)((this.Value >> 8) ^ table[tabPtr]);
			}
		}

		protected override byte[] HashFinal()
		{
			return new byte[] {
				(byte)((this.Value >> 8) & 0xFF),
				(byte)((this.Value >> 0) & 0xFF)
			};
		}
	}
}
